#include "TcpServer.h"
#include "InetAddress.h"
#include "Acceptor.h"
#include "EventLoop.h"
#include "EventLoopThreadPool.h"
#include "Logger.h"
#include "TcpConnection.h"

// 检查baseloop是否为空
static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
    if (loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d: baseloop is null", __FILE__, __FUNCTION__, __LINE__);
    }
    return loop;
}

TcpServer::TcpServer(EventLoop *loop,
                     const InetAddress &listenAddr,
                     const std::string &nameArg,
                     Option option)
    : loop_(CheckLoopNotNull(loop))
    , ipPort_(listenAddr.toIpPort())
    , name_(nameArg)
    , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
    , threadPool_(new EventLoopThreadPool(loop, name_))
    , connectionCallback_()
    , messageCallback_()
    , nextConnId_(1)
    , taskId_(1)
    , started_(0)
    , enableInactiveRelease_(false)
{
    // 注册对新连接的处理函数
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this,
                                                  std::placeholders::_1,
                                                  std::placeholders::_2));
}

TcpServer::~TcpServer()
{
    LOG_INFO("TcpServer::~TcpServer [%s]", name_.c_str());
    for (auto &item : connevtions_)
    {
        TcpConnectionPtr conn(item.second); // 先构造记录一下，出了大括号自动销毁，否则reset后就找不到TcpConnection对象了

        item.second.reset();
        conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
    }
}

// 设置subloop的个数
void TcpServer::setThreadNum(int numThreads)
{
    threadPool_->setThreadNum(numThreads);
}

// 启动服务端监听
void TcpServer::start()
{
    if (started_++ == 0)
    {
        threadPool_->start(threadInitCallbcak_); // 启动线程池，创建新线程，线程函数创建subloop并启动loop循环
        /**
         * 主函数所在线程启动 listen套接字的监听，即托管给baseloop的poller.有新连接时执行handleRead,
         * handleRead里执行newaconnectioncallback函数也就是newConnection，对connfd打包channel，选择subloop，分发channel
         */
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
    }
}

//手动添加定时任务
void TcpServer::runAfter(const Functor& task, int delay)
{
    loop_->runInLoop(std::bind(&TcpServer::runAfterInLoop, this, task, delay));
}

void TcpServer::runAfterInLoop(const Functor& task, int delay)
{
    taskId_++;
    loop_->timerAdd(std::to_string(taskId_), delay, task);
}


void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
    EventLoop *ioLoop = threadPool_->getNextLoop(); // 选择一个subloop
    char buf[64];
    snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_++);
    std::string connName = name_ + buf; // 新连接的名字
    LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s",
             name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());

    struct sockaddr_in local;
    bzero(&local, sizeof local);
    socklen_t len = sizeof local;
    if (getsockname(sockfd, (struct sockaddr *)&local, &len) < 0)
    {
        LOG_ERROR("%s:%s:%d - getsockname() error: %d: %s", __FILE__, __FUNCTION__, __LINE__, errno, strerror(errno));
    }
    InetAddress localAddr(local);

    // 给新连接的sockfd 创建对应的TcpConnection对象
    TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
    connevtions_[connName] = conn; // 添加键值对
    // 回调函数，用户=>TcpServer=>TcpConnection=>Channel-->poller--->通知channel调用回调
    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);
    conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
    if(enableInactiveRelease_)
        conn->enableInactiveRelease(timeout_);  //设置非活跃销毁

    // 调用函数，主要是对新连接channel设置epollin
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{
    LOG_INFO("TcpServer::removeConnectionInLoop [%s] - connection %s", name_.c_str(), conn->name().c_str());
    connevtions_.erase(conn->name());
}